#pragma once
#include <cstdint>
#include "Util.h"
namespace xncc {
namespace msg {
    using queue_size_t = uint64_t;
    template <typename MESSAGE, queue_size_t SIZE>
    class SpscQueue
    {
        struct __attribute__((aligned(64))) Block
        {
            MESSAGE msg;
        };

        static constexpr bool is_power_of_2(queue_size_t n) { return (n != 0 && ((n & (n - 1)) == 0)); }

        static constexpr queue_size_t MASK = SIZE - 1;

      public:
        static SpscQueue* create()
        {
            void* mem = aligned_alloc(64, sizeof(SpscQueue));
            return new (mem) SpscQueue();
        }

        static void destroy(SpscQueue* queue)
        {
            queue->~SpscQueue();
            free(queue);
        }

        ~SpscQueue() = default;

        SpscQueue()
        {
            static_assert(is_power_of_2(SIZE), "SIZE must be power of 2");
            memset(data, 0, sizeof(Block) * SIZE);
        }
        MESSAGE* getWriteMessage()
        {
            if (MASK == ((write_index_ - read_index_) & MASK)) {
                return nullptr;
            }
            return &data[write_index_].msg;
        }
        void finishWrite()
        {
            __asm__ __volatile__("" ::: "memory");
            write_index_ = (write_index_ + 1) & MASK;
        }
        const MESSAGE* getReadMessage()
        {
            if (write_index_ == read_index_) {
                return nullptr;
            }
            return &data[read_index_].msg;
        }
        void finishRead()
        {
            __asm__ __volatile__("" ::: "memory");
            read_index_ = (read_index_ + 1) & MASK;
        }

      private:
        // volatile 防止编译器优化
        volatile __attribute__((aligned(64))) queue_size_t read_index_  = 0;
        volatile __attribute__((aligned(64))) queue_size_t write_index_ = 0;
        Block                                              data[SIZE];
    };

}  // namespace msg
}  // namespace xncc